查看原文
其他

Java丨Ele实验室“疫情传播仿真程序”的代码实现

龙中华 博文视点Broadview 2020-10-17


小编说:本文由博文视点一位身在武汉的作者龙中华老师,根据Ele实验室发布的“疫情传播仿真程序”视频以及其开源代码整理编辑而成首发于个人博客:

https://blog.csdn.net/u013840066/article/details/104212919

前几天,“Ele实验室”  Bruce Young同学在家制作了一个有关病毒传播的仿真程序,为我们带来了极大的视角上的震撼,对于人们认识病毒传播有很大的价值,于是这里把源代码分享出来(版权归Bruce Young同学),因为该程序实际上没有实际的商用价值(有很大的传播教育价值),所以我们只做一些简单的讲解。

视频中程序代码GitHub开源链接:

https://github.com/KikiLetGo/VirusBroadcast/tree/master/src



下面我们进入正题,运行效果图如下图所示:



该程序主要使用Swing( 一个为Java设计的GUI工具包)来绘制图形用户界面(GUI)。实现的步骤如下:

创建Point 类

该类用于定义绘制图形界面上的点,代码如下:

1public class Point {
2    private int x;
3    private int y;
4
5    public Point(int x, int y) {
6        this.x = x;
7        this.y = y;
8    }
9
10    public int getX() {
11        return x;
12    }
13
14    public void setX(int x) {
15        this.x = x;
16    }
17
18    public int getY() {
19        return y;
20    }
21
22    public void setY(int y) {
23        this.y = y;
24    }
25}


创建常量类

该类用于调整参数来展现不同的效果,见下方代码:

1public class Constants {
2    //初始感染数量
3    public static int ORIGINAL_COUNT=50;
4    //传播率
5    public static float BROAD_RATE = 0.8f;
6    //潜伏时间
7    public static float SHADOW_TIME = 140;
8    //医院收治响应时间
9    public static int HOSPITAL_RECEIVE_TIME=10;
10    //医院床位
11    public static int BED_COUNT=1000;
12    //流动意向平均值
13    public static float u=-0.99f;
14}


创建城市类

该类用于定义一个城市。

1public class City {
2    private int centerX;
3    private int centerY;
4
5    public City(int centerX, int centerY) {
6        this.centerX = centerX;
7        this.centerY = centerY;
8    }
9
10    public int getCenterX() {
11        return centerX;
12    }
13
14    public void setCenterX(int centerX) {
15        this.centerX = centerX;
16    }
17
18    public int getCenterY() {
19        return centerY;
20    }
21
22    public void setCenterY(int centerY) {
23        this.centerY = centerY;
24    }
25}


创建医院类

该类用于创建一个演示的医院类。

1public class Hospital {
2    private int x=800;
3    private int y=110;
4    private int width;
5    private int height=606;
6    public int getWidth() {
7        return width;
8    }
9    public int getHeight() {
10        return height;
11    }
12
13    public int getX() {
14        return x;
15    }
16
17    public int getY() {
18        return y;
19    }
20
21    private static Hospital hospital = new Hospital();
22    public static Hospital getInstance(){
23        return hospital;
24    }
25    private Point point = new Point(800,100);
26    private List<Bed> beds = new ArrayList<>();
27
28    private Hospital() {
29        if(Constants.BED_COUNT==0){
30            width=0;
31            height=0;
32        }
33        int column = Constants.BED_COUNT/100;
34        width = column*6;
35
36        for(int i=0;i<column;i++){
37
38            for(int j=10;j<=610;j+=6){
39                Bed bed = new Bed(point.getX()+i*6,point.getY()+j);
40                beds.add(bed);
41
42            }
43
44        }
45    }
46
47    public Bed pickBed(){
48        for(Bed bed:beds){
49            if(bed.isEmpty()){
50                return bed;
51            }
52        }
53        return null;
54    }
55}


创建医院的床位类

该类创建一个用于演示的医院的病床类。

1public class Bed extends Point{
2    public Bed(int x, int y) {
3        super(x, y);
4    }
5    private boolean isEmpty=true;
6
7    public boolean isEmpty() {
8        return isEmpty;
9    }
10
11    public void setEmpty(boolean empty) {
12        isEmpty = empty;
13    }
14}


创建PersonPool类

该类创建PersonPool,用于管理城市大小和人数。

1public class PersonPool {
2    private static PersonPool personPool = new PersonPool();
3    public static PersonPool getInstance(){
4        return personPool;
5    }
6
7    List<Person> personList = new ArrayList<Person>();
8
9    public List<Person> getPersonList() {
10        return personList;
11    }
12
13    private PersonPool() {
14        City city = new City(400,400);
15        for (int i = 0; i < 5000; i++) {
16            Random random = new Random();
17            int x = (int) (100 * random.nextGaussian() + city.getCenterX());
18            int y = (int) (100 * random.nextGaussian() + city.getCenterY());
19            if(x>700){
20                x=700;
21            }
22            Person person = new Person(city,x,y);
23            personList.add(person);
24        }
25    }
26}


创建Person类

1public class Person {
2    private City city;
3    private int x;
4    private int y;
5    private MoveTarget moveTarget;
6    int sig=1;
7
8
9    double targetXU;
10    double targetYU;
11    double targetSig=50;
12
13
14    public interface State{
15        int NORMAL = 0;
16        int SUSPECTED = NORMAL+1;
17        int SHADOW = SUSPECTED+1;
18
19        int CONFIRMED = SHADOW+1;
20        int FREEZE = CONFIRMED+1;
21        int CURED = FREEZE+1;
22    }
23
24    public Person(City city, int x, int y) {
25        this.city = city;
26        this.x = x;
27        this.y = y;
28        targetXU = 100*new Random().nextGaussian()+x;
29        targetYU = 100*new Random().nextGaussian()+y;
30
31    }
32    public boolean wantMove(){
33        double value = sig*new Random().nextGaussian()+Constants.u;
34        return value>0;
35    }
36
37    private int state=State.NORMAL;
38
39    public int getState() {
40        return state;
41    }
42
43    public void setState(int state) {
44        this.state = state;
45    }
46
47    public int getX() {
48        return x;
49    }
50
51    public void setX(int x) {
52        this.x = x;
53    }
54
55    public int getY() {
56        return y;
57    }
58
59    public void setY(int y) {
60        this.y = y;
61    }
62    int infectedTime=0;
63    int confirmedTime=0;
64    public boolean isInfected(){
65        return state>=State.SHADOW;
66    }
67    public void beInfected(){
68        state = State.SHADOW;
69        infectedTime=MyPanel.worldTime;
70    }
71
72    public double distance(Person person){
73        return Math.sqrt(Math.pow(x-person.getX(),2)+Math.pow(y-person.getY(),2));
74    }
75
76    private void freezy(){
77        state = State.FREEZE;
78    }
79    private void moveTo(int x,int y){
80        this.x+=x;
81        this.y+=y;
82    }
83    private void action(){
84        if(state==State.FREEZE){
85            return;
86        }
87        if(!wantMove()){
88            return;
89        }
90        if(moveTarget==null||moveTarget.isArrived()){
91
92            double targetX = targetSig*new Random().nextGaussian()+targetXU;
93            double targetY = targetSig*new Random().nextGaussian()+targetYU;
94            moveTarget = new MoveTarget((int)targetX,(int)targetY);
95
96        }
97
98
99        int dX = moveTarget.getX()-x;
100        int dY = moveTarget.getY()-y;
101        double length=Math.sqrt(Math.pow(dX,2)+Math.pow(dY,2));
102
103        if(length<1){
104            moveTarget.setArrived(true);
105            return;
106        }
107        int udX = (int) (dX/length);
108        if(udX==0&&dX!=0){
109            if(dX>0){
110                udX=1;
111            }else{
112                udX=-1;
113            }
114        }
115        int udY = (int) (dY/length);
116        if(udY==0&&udY!=0){
117            if(dY>0){
118                udY=1;
119            }else{
120                udY=-1;
121            }
122        }
123
124        if(x>700){
125            moveTarget=null;
126            if(udX>0){
127                udX=-udX;
128            }
129        }
130        moveTo(udX,udY);
131
132//        if(wantMove()){
133//        }
134
135
136    }
137
138    private float SAFE_DIST = 2f;
139
140    public void update(){
141        //@TODO找时间改为状态机
142        if(state>=State.FREEZE){
143            return;
144        }
145        if(state==State.CONFIRMED&&MyPanel.worldTime-confirmedTime>=Constants.HOSPITAL_RECEIVE_TIME){
146            Bed bed = Hospital.getInstance().pickBed();
147            if(bed==null){
148                System.out.println("隔离区没有空床位");
149            }else{
150                state=State.FREEZE;
151                x=bed.getX();
152                y=bed.getY();
153                bed.setEmpty(false);
154            }
155        }
156        if(MyPanel.worldTime-infectedTime>Constants.SHADOW_TIME&&state==State.SHADOW){
157            state=State.CONFIRMED;
158            confirmedTime = MyPanel.worldTime;
159        }
160
161        action();
162
163        List<Person> people = PersonPool.getInstance().personList;
164        if(state>=State.SHADOW){
165            return;
166        }
167       for(Person person:people){
168           if(person.getState()== State.NORMAL){
169               continue;
170           }
171           float random = new Random().nextFloat();
172           if(random<Constants.BROAD_RATE&&distance(person)<SAFE_DIST){
173               this.beInfected();
174           }
175       }
176    }
177}


创建MyPanel类

1public class MyPanel extends JPanel implements Runnable {
2
3
4   private int pIndex=0;
5
6    public MyPanel() {
7        this.setBackground(new Color(0x444444));
8    }
9
10    @Override
11    public void paint(Graphics arg0) {
12        super.paint(arg0);
13        //draw border
14        arg0.setColor(new Color(0x00ff00));
15        arg0.drawRect(Hospital.getInstance().getX(),Hospital.getInstance().getY(),
16                Hospital.getInstance().getWidth(),Hospital.getInstance().getHeight());
17
18
19
20        List<Person> people = PersonPool.getInstance().getPersonList();
21        if(people==null){
22            return;
23        }
24        people.get(pIndex).update();
25        for(Person person:people){
26
27            switch (person.getState()){
28                case Person.State.NORMAL:{
29                    arg0.setColor(new Color(0xdddddd));
30
31                }break;
32                case Person.State.SHADOW:{
33                    arg0.setColor(new Color(0xffee00));
34
35                }break;
36                case Person.State.CONFIRMED:
37                case Person.State.FREEZE:{
38                    arg0.setColor(new Color(0xff0000));
39
40                }break;
41            }
42            person.update();
43            arg0.fillOval(person.getX(), person.getY(), 33);
44
45        }
46        pIndex++;
47        if(pIndex>=people.size()){
48            pIndex=0;
49        }
50    }
51
52    public static int worldTime=0;
53    @Override
54    public void run() {
55        while (true) {
56
57            this.repaint();
58
59            try {
60                Thread.sleep(100);
61                worldTime++;
62            } catch (InterruptedException e) {
63                e.printStackTrace();
64            }
65        }
66
67    }
68
69
70}


创建MoveTarget 类

创建MoveTarget 类用于模拟人群流动。

1public class MoveTarget {
2    private int x;
3    private int y;
4    private boolean arrived=false;
5
6    public MoveTarget(int x, int y) {
7        this.x = x;
8        this.y = y;
9    }
10
11    public int getX() {
12        return x;
13    }
14
15    public void setX(int x) {
16        this.x = x;
17    }
18
19    public int getY() {
20        return y;
21    }
22
23    public void setY(int y) {
24        this.y = y;
25    }
26
27    public boolean isArrived() {
28        return arrived;
29    }
30
31    public void setArrived(boolean arrived) {
32        this.arrived = arrived;
33    }
34}


修改入口类

修改入口类,绘制图形

1import javax.swing.*;
2import java.awt.image.BufferedImage;
3import java.util.ArrayList;
4import java.util.List;
5import java.util.Random;
6
7public class Main {
8 public static void main(String[] args) {
9        MyPanel p = new MyPanel();
10        Thread panelThread = new Thread(p);
11        JFrame frame = new JFrame();
12        frame.add(p);
13        frame.setSize(1000800);
14        frame.setLocationRelativeTo(null);
15        frame.setVisible(true);
16        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17        panelThread.start();
18
19        List<Person> people = PersonPool.getInstance().getPersonList();
20        for(int i=0;i<Constants.ORIGINAL_COUNT;i++){
21            int index = new Random().nextInt(people.size()-1);
22            Person person = people.get(index);
23
24            while (person.isInfected()){
25                index = new Random().nextInt(people.size()-1);
26                person = people.get(index);
27            }
28            person.beInfected();
29        }
30    }
31}


到此完成,运行程序即可。



图书推荐

《Spring Boot实战派》

龙中华 著


  • 让开发像搭积木一样简单,在实战情景中学习,学完即知怎么实战

  版本点新  针对 Spring Boot 2.0 及以上版本

  体例科学  用“知识点+实例”形式编写  实例丰富  58个基础实例 + 2个综合项目  对比选优  对比讲解多种同类技术,便于技术选型,如 Spring Security 和 Shiro、Elasticsearch 和 Solr、JPA 和 Mybatis  技术点新  讲解了时下流行的接口架构风格 RESTful 、用来实现高并发的 Redis 、以及用来实现系统间通信的中间件 RabbitMQ

关于作者

龙中华

12年来一直在某一线互联网公司担任资深系统分析师。

目前带领3个研发团队,承担系统的分析、设计、实施、演进,以及技术团队管理和培训等职责。有独到的团队建设和管理经验,对互联网多种技术特点和发展趋势有较深入的研究,对多种技术(如 Spring Boot 、Spring Cloud 和 Service Mesh )有深入的研究和实战经验。



(扫码了解本书详情)





如果喜欢本文
欢迎 在看留言分享至朋友圈 三连

点击下方入口,获取学院精品课,宅家期间让我们共同学习进阶!

  热文推荐  






▼ 点击阅读原文,访问作者博客。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存